package com.gs.synedocket.util;


import static com.gs.synedocket.constant.AllConstants.TAG;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebSettings;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.gs.synedocket.apiresult.ApiResultData;
import com.gs.synedocket.callback.Callback;
import com.gs.synedocket.callback.CallbackWithProgress;
import com.gs.synedocket.constant.AllConstants;
import com.gs.synedocket.db.dao.HttpCacheUtil;
import com.gs.synedocket.db.domain.HttpCache;

import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Dispatcher;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;

public class OKHttpUtil {
    private static volatile OKHttpUtil instance;
    private static final long MAX_SIZE = 8 * 1024 * 1024;
    public final static String NET_URL = "http://synergosdemo.ddns.net/synedocket";
//    public final static String NET_URL = "http://192.168.1.188:3001";
//    public final static String NET_URL = "https://app.kwcml.com/eDocketRMC_UAT";
//    public final static String NET_URL = "http://192.168.1.188:8001";
    public final static String SIGNAL_URL = "http://192.168.1.188:6001";
//    public final static String SIGNAL_URL = "https://app.kwcml.com/eDocketRMC_UAT/msgSvr";
    public final static String IMG_URL = NET_URL + "/api/utils/images/400/";
    private static final String VERSION_TOKEN = "56efbbf00223e8872e935752c2537b88";
    public OkHttpClient okHttpClient;
    public static Handler handler;
    private final String imei;
    private String mUserAgent;
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public Handler getHandler() {
        return handler;
    }

    public OKHttpUtil() {
        imei = AppUtil.getInstance().getIMEI();
        handler = new Handler(Looper.getMainLooper());
        File dir = AppUtil.getInstance().getContext().getFilesDir();
        Cache cache = new Cache(dir, MAX_SIZE);
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(30);
        dispatcher.setMaxRequestsPerHost(10);
        okHttpClient = new OkHttpClient.Builder()
                .dispatcher(dispatcher)
                .cache(cache)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .callTimeout(60, TimeUnit.SECONDS)
                .addInterceptor(new CacheInterceptor())
                .addInterceptor(new TokenInterceptor())
                .addNetworkInterceptor(new HttpCacheInterceptor())
                .addInterceptor(new ParamInterceptor())
                .retryOnConnectionFailure(false)
                .build();
    }

    public static OKHttpUtil getInstance() {
        if (instance == null) {
            synchronized (OKHttpUtil.class) {
                if (instance == null) {
                    instance = new OKHttpUtil();
                }
            }
        }
        return instance;
    }

    private String getUserAgent() {
        if (TextUtils.isEmpty(mUserAgent)) {
            String userAgent;
            try {
                userAgent = WebSettings.getDefaultUserAgent(AppUtil.getInstance().getContext());
            } catch (Exception e) {
                userAgent = System.getProperty("http.agent");
            }
            StringBuilder sb = new StringBuilder();
            for (int i = 0, length = userAgent.length(); i < length; i++) {
                char c = userAgent.charAt(i);
                if (c <= '\u001f' || c >= '\u007f') {
                    sb.append(String.format("\\u%04x", (int) c));
                } else {
                    sb.append(c);
                }
            }
            mUserAgent = sb.toString();
        }
        return mUserAgent;
    }

    public void getVersion(final String url, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            Log.i(TAG, "getVersion: " + url);
            Request request = new Request.Builder()
                    .get()
                    .addHeader("token", VERSION_TOKEN)
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .url(url)
                    .build();
            Call call = okHttpClient.newCall(request);
            call.enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    handler.post(() -> callback.onFailed(e));
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        String result = response.body().string();
                        Log.i(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            handler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            handler.post(() -> {
                                try {
                                    Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                    callback.onSuccess(o);
                                } catch (Exception e) {
                                    callback.onFailed(e);
                                }
                            });
                        }
                    } catch (IOException e) {
                        handler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }

    public void getAsync(final String url, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            Log.d(TAG, url);
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    handler.post(() -> callback.onFailed(e));
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            handler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            handler.post(() -> {
                                try {
                                    Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                    callback.onSuccess(o);
                                } catch (Exception e) {
                                    callback.onFailed(e);
                                    e.printStackTrace();
                                }
                            });
                        }
                    } catch (IOException e) {
                        handler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }

    public void getSync(final String url, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            Log.d(TAG, url);
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    callback.onFailed(e);
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                callback.onSuccess(o);
                            } catch (Exception e) {
                                callback.onFailed(e);
                                e.printStackTrace();
                            }
                        }
                    } catch (IOException e) {
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    public void getSyncThread(final String url, final Callback callback, final Class c) {
        ThreadManager.getInstance().execute(() -> {
            Log.d(TAG, url);
            Request request = new Request.Builder()
                    .url(url)
                    .get()
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    callback.onFailed(e);
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                ApiResultData<List<Object>> apiResultData = new ApiResultData<>();
                                List<Object> objects = new ArrayList<>();
                                JSONObject jsonObject = new JSONObject(result);
                                JSONArray data = jsonObject.optJSONArray("data");
                                if (data != null) {
                                    JsonArray array = JsonParser.parseString(data.toString()).getAsJsonArray();
                                    for (JsonElement jsonElement : array) {
                                        objects.add(GsonUtil.getInstance().fromJson(jsonElement, c));
                                    }
                                }
                                apiResultData.setStatus(jsonObject.optInt("status"));
                                apiResultData.setMessage(jsonObject.optString("message"));
                                apiResultData.setData(objects);
                                callback.onSuccess(apiResultData);
                            } catch (Exception e) {
                                callback.onFailed(e);
                                e.printStackTrace();
                            }
                        }
                    } catch (IOException e) {
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    public void postAsync(String url, Object json, Callback callback, Type typeofT) {
        postAsync(url, GsonUtil.getInstance().toJson(json), callback, typeofT);
    }

    public void postAsync(final String url, final String json, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            RequestBody body = RequestBody.create(json, JSON);
            Log.d(TAG, json);
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .post(body)
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    handler.post(() -> {
                        Log.d(TAG, "result fail");
                        callback.onFailed(e);
                        e.printStackTrace();
                    });
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(TAG, "result empty");
                            handler.post(() -> callback.onFailed(new IOException()));
                        } else {
                            handler.post(() -> {
                                try {
                                    Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                    callback.onSuccess(o);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    callback.onFailed(e);
                                }
                            });
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        handler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }

    public void postSync(String url, Object json, Callback callback, Type typeofT) {
        postSync(url, GsonUtil.getInstance().toJson(json), callback, typeofT);
    }

    public void postSync(final String url, final String json, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            RequestBody body = RequestBody.create(json, JSON);
            Log.d(TAG, json);
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .post(body)
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    Log.d(TAG, "result fail");
                    callback.onFailed(e);
                    e.printStackTrace();
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(TAG, "result empty");
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                callback.onSuccess(o);
                            } catch (Exception e) {
                                e.printStackTrace();
                                callback.onFailed(e);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    public void postSyncThread(final String url, final String json, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            RequestBody body = RequestBody.create(json, JSON);
            Log.d(TAG, json);
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .post(body)
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    Log.d(TAG, "result fail");
                    callback.onFailed(e);
                    e.printStackTrace();
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) {
                    try {
                        final String result = response.body().string();
                        Log.d(TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            Log.d(TAG, "result empty");
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                callback.onSuccess(o);
                            } catch (Exception e) {
                                e.printStackTrace();
                                callback.onFailed(e);
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    /**
     * 上傳文件
     *
     * @param url
     * @param filePath
     * @param callback
     * @param typeofT
     */
    public void uploadFileSync(String url, String filePath, final Callback callback, final Type typeofT) {
        ThreadManager.getInstance().execute(() -> {
            File file = new File(filePath);
            RequestBody fileBody = RequestBody.Companion.create(file, MediaType.parse("application/octet-stream"));
            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(), fileBody)
                    .build();
            final Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .post(requestBody)
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    Log.d(TAG, "uploadFileAsync:onFailure");
                    callback.onFailed(e);
                }

                @Override
                public void onResponse(@NotNull final Call call, @NotNull Response response) {
                    try {
                        String result = response.body().string();
                        Log.d(TAG, "uploadFileAsync:" + result);
                        if (TextUtils.isEmpty(result)) {
                            callback.onFailed(new IOException());
                        } else {
                            try {
                                Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                callback.onSuccess(o);
                            } catch (Exception e) {
                                e.printStackTrace();
                                callback.onFailed(e);
                            }
                        }
                    } catch (IOException e) {
                        callback.onFailed(e);
                    }
                }
            });
        });
    }

    public void downloadFile(final String url, final File file, final CallbackWithProgress callback) {
        Log.i(TAG, "downloadFile");
        ThreadManager.getInstance().execute(() -> {
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("token", VERSION_TOKEN)
                    .addHeader("ViewModule", AllConstants.VIEW_MODULE)
                    .addHeader("imei", imei)
                    .addHeader("User-Agent", getUserAgent())
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .get()
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    handler.post(() -> callback.onFailed(e));
                }

                @Override
                public void onResponse(@NotNull final Call call, @NotNull Response response) {
                    InputStream is = null;
                    OutputStream os = null;
                    try {
                        final long totalLen = response.body().contentLength();
                        is = response.body().byteStream();
                        os = new FileOutputStream(file);

                        byte[] buffer = new byte[8 * 1024];
                        int curLen = 0;
                        int bufferLen;
                        while (!call.isCanceled() && (bufferLen = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bufferLen);
                            os.flush();
                            curLen += bufferLen;

                            final int finalCurLen = curLen;
                            handler.post(() -> callback.onProgress((int) (finalCurLen * 1.0f / totalLen * 100)));
                        }
                        if (call.isCanceled()) {
                            handler.post(() -> callback.onFailed(new IOException()));
                            return;
                        }
                        file.setExecutable(true, false);
                        file.setReadable(true, false);
                        file.setWritable(true, false);
                        handler.post(() -> callback.onSuccess(file));
                    } catch (final Exception e) {
//                        if (call.isCanceled()) {
//                            return;
//                        }
                        e.printStackTrace();
                        handler.post(() -> callback.onFailed(e));
                    } finally {
                        try {
                            if (is != null) {
                                is.close();
                            }
                            if (os != null) {
                                os.close();
                            }
                        } catch (IOException e) {
                            handler.post(() -> callback.onFailed(e));
                            e.printStackTrace();
                        }
                    }
                }
            });
        });
    }

    public void downloadFileMulti(final String url, final File file, final CallbackWithProgress callback) {
        Log.i(TAG, "downloadFileMulti");
        ThreadManager.getInstance().execute(() -> {
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("token", VERSION_TOKEN)
                    .addHeader("ViewModule", "app")
                    .addHeader("imei", imei)
                    .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                    .get()
                    .build();
            okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                    Log.i(TAG, "onFailure: ");
                    handler.post(() -> callback.onFailed(e));
                }

                @Override
                public void onResponse(@NotNull final Call call, @NotNull Response response) {
                    try {
                        final long totalLen = response.body().contentLength();
                        Log.i(TAG, "onResponse: " + totalLen);
                        RandomAccessFile raf = new RandomAccessFile(file, "rwd");
                        raf.setLength(totalLen);
                        raf.close();

                        int threadNum = 5;
                        long blockSize = totalLen / threadNum;

                        DownloadRunning[] downloadRunnings = new DownloadRunning[threadNum];
                        for (int i = 0; i < threadNum; i++) {
                            long start = i * blockSize;
                            long end;
                            if (i == threadNum - 1) {
                                end = totalLen;
                            } else {
                                end = (i + 1) * blockSize;
                            }
                            Log.i(TAG, i + ": " + "bytes" + start + "-" + end);
                            DownloadRunning running = new DownloadRunning(i, start, end, url, file, VERSION_TOKEN, imei);
                            ThreadManager.getInstance().execute(running);
                            downloadRunnings[i] = running;
                        }

                        boolean isFinish = false;
                        int downloadSize;
                        while (!isFinish) {
                            isFinish = true;
                            downloadSize = 0;
                            for (DownloadRunning downloadRunning : downloadRunnings) {
                                downloadSize += downloadRunning.getCurLen();
                                if (downloadRunning.isFail()) {
                                    handler.post(() -> callback.onFailed(new IOException()));
                                    return;
                                }
                                if (!downloadRunning.isFinish()) {
                                    isFinish = false;
                                }
                            }
                            int progress = (int) (downloadSize * 1.0f / totalLen * 100);
                            handler.post(() -> callback.onProgress(progress));
                            try {
                                Thread.sleep(1000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        handler.post(() -> callback.onSuccess(file));
                    } catch (Exception e) {
                        handler.post(() -> callback.onFailed(e));
                    }
                }
            });
        });
    }
}

class DownloadRunning implements Runnable {

    private static final int BUFFER_SIZE = 4*1024;
    private final int threadId;
    private final long start;
    private final long end;
    private final String url;
    private final File file;
    private int curLen;
    private boolean finish;
    private boolean fail;
    private final String token;
    private final String imei;

    public DownloadRunning(int threadId, long start, long end, String url, File file, String token, String imei) {
        this.threadId = threadId;
        this.start = start;
        this.end = end;
        this.url = url;
        this.file = file;
        this.token = token;
        this.imei = imei;
    }

    public int getCurLen() {
        return curLen;
    }

    public boolean isFinish() {
        return finish;
    }

    public boolean isFail() {
        return fail;
    }

    @Override
    public void run() {
        Request request = new Request.Builder()
                .url(url)
                .addHeader("token", token)
                .addHeader("ViewModule", "app")
                .addHeader("imei", imei)
                .addHeader("range", "bytes" + start + "-" + end)
                .addHeader("AppVersion", AppUtil.getInstance().getVersion())
                .get()
                .build();
        OKHttpUtil.getInstance().okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull final IOException e) {
            }

            @Override
            public void onResponse(@NotNull final Call call, @NotNull Response response) {
                BufferedInputStream is = null;
                RandomAccessFile raf = null;
                try {
                    ResponseBody body = response.body();
                    if (body == null) {
                        fail = true;
                        return;
                    }
                    Log.i(TAG, threadId + "内容长度: " + body.contentLength());
                    is = new BufferedInputStream(body.byteStream());
                    raf = new RandomAccessFile(file, "rwd");
                    raf.seek(start);
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int bufferLen;
                    while (!call.isCanceled() && (bufferLen = is.read(buffer, 0, BUFFER_SIZE)) != -1) {
                        raf.write(buffer, 0, bufferLen);
                        curLen += bufferLen;
                    }
                    Log.i(TAG, threadId + "curLen：" + curLen);
                    if (call.isCanceled() && curLen != (end - start)) {
                        fail = true;
                        return;
                    }
                    finish = true;
                    Log.i(TAG, threadId + "：success");
                } catch (final Exception e) {
                    e.printStackTrace();
                    fail = true;
                } finally {
                    try {
                        if (is != null) {
                            is.close();
                        }
                        if (raf != null) {
                            raf.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        fail = true;
                    }
                }
            }
        });
    }
}

class CacheInterceptor implements Interceptor {

    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Request request = chain.request();
        Response response;
        String url = request.url().toString();
        if (url.equals(OKHttpUtil.NET_URL + "/api/users/login")
                || url.equals(OKHttpUtil.NET_URL + "/api/users/zvuhvibgljsv")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/users/forgotPwd?userName=")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/users/logOut?userId=")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/WebServices/download/")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/WebServices/appInfo?applicationId=")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/utils/uploadfile")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/utils/uploadfilebase64")
                || url.startsWith(OKHttpUtil.NET_URL + "/api/syns")) {
            response = getResponse(chain, CacheControl.FORCE_NETWORK);
        } else {
            if (request.method().equals("POST")) {
                Buffer buffer = new Buffer();
                request.body().writeTo(buffer);
                String params = buffer.readString(Charset.forName("UTF-8"));
                if (NetworkUtil.getInstance().isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        MediaType type = originalResponse.body().contentType();
                        byte[] bs = originalResponse.body().bytes();
                        response = originalResponse.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", "public, max-age=" + 60 * 60 * 24)
                                .body(ResponseBody.Companion.create(bs, type))
                                .build();
                        HttpCacheUtil httpCacheUtil = new HttpCacheUtil();
                        httpCacheUtil.insertOrReplaceCache(new HttpCache(null, url, params, new String(bs, "GB2312")));
                    } catch (IOException e) {
                        response = getCacheResponse(url, params, request);
                        e.printStackTrace();
                    }
                } else {
                    response = getCacheResponse(url, params, request);
                }
            } else {
                if (NetworkUtil.getInstance().isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        originalResponse.close();
                        response = getResponse(chain, CacheControl.FORCE_NETWORK);
                    } catch (IOException e) {
                        response = getResponse(chain, CacheControl.FORCE_CACHE);
                        e.printStackTrace();
                    }
                } else {
                    response = getResponse(chain, CacheControl.FORCE_CACHE);
                }
            }
        }
        return response;
    }

    private Response getCacheResponse(String url, String params, Request request) {
        HttpCacheUtil httpCacheUtil = new HttpCacheUtil();
        String b = httpCacheUtil.getHttpCache(url, params);
        return new Response.Builder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 28)
                .body(ResponseBody.Companion.create(b.getBytes(), OKHttpUtil.JSON))
                .request(request)
                .protocol(Protocol.HTTP_1_1)
                .code(200)
                .message("")
                .build();
    }

    private Response getResponse(Chain chain, CacheControl forceCache) throws IOException {
        return chain.proceed(chain.request()
                .newBuilder()
                .cacheControl(forceCache)
                .build()
        );
    }
}

class HttpCacheInterceptor implements Interceptor {

    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, max-age=" + 24 * 30 * 6000)
                .build();
    }
}